﻿using System;
using System.Net;
using System.Threading;
using System.Reflection;
using System.Diagnostics;

/* APM DEMO
 *
 * This project demonstrates how to use and write asynchronous methods, in the context of a 
 * simple TCP server application.
 * 
 * The server continually listens for client connections. When it receives a connection, it
 * reads 5000 bytes, reverses the bytes, and sends the result back to the client.
 * 
 * There are five implementations of the server:
 * 
 *   1. Blocking\Server.cs                  - uses synchronous (blocking) networking methods
 *   2. Nonblocking\Server.cs               - uses asynchronous (non-blocking) networking methods
 *   3. Nonblocking TPL\Server.cs           - uses asynchronous (non-blocking) networking methods with TPL's FromAsync
 *   4. ExtensibleNonblocking\Server.cs     - demonstrates how to *write* asynchronous methods
 *   5. ExtensibleNonblockingTPL\Server.cs  - demonstrates how to *write* asynchronous methods with TPL's FromAync
 *   
 * To switch between each of the five servers, edit the StartServer() method.
 * 
 * There are two client simulators:
 *   1. SimulateLotsOfClients       - runs a few hundred fast clients
 *   2. SimulateFastAndSlowClients  - runs a few hundred slow clients followed by 10 fast clients
 * 
 * The second client simulator really shows the benefit of APM over ordinary blocking methods: notice how the
 * APM ensures that the fast clients are not delayed by the slow clients!
 */

namespace AsynchronousMethods
{
  class Program
  {
    public const int TcpPort = 51111;
    public const int MessageLength = 5000;

    static void Main (string [] args)
    {
      if (args.Length > 0 && args [0] == "client")
      {
        //SimulateLotsOfClients();
        SimulateFastAndSlowClients ();
        Console.ReadLine ();
        return;
      }

      // Start the TCP server on a separate thread
      Thread t = new Thread (StartServer);
      t.IsBackground = true;
      t.Start ();

      Thread.Sleep (200);       // Give server has time to come online

      // Launch a separate process to simulate clients
      Process.Start (Assembly.GetExecutingAssembly ().CodeBase, "client");

      Console.WriteLine ("Server started: press Enter to exit");
      Console.ReadLine ();
    }

    static void StartServer ()
    {
      // Uncomment one of the lines below:

      //new Blocking.Server ().Serve (IPAddress.Any, TcpPort);
      //new Nonblocking.Server ().Serve (IPAddress.Any, TcpPort);
      //new NonblockingTPL.Server ().Serve (IPAddress.Any, TcpPort);
      //new ExtensibleNonblocking.Server ().Serve (IPAddress.Any, TcpPort);
      new ExtensibleNonblockingTPL.Server ().Serve (IPAddress.Any, TcpPort);
    }

    static void SimulateLotsOfClients ()
    {
      for (int i = 0; i < 100 * Environment.ProcessorCount; i++)
        new Thread (SimulateClient).Start (false);
    }

    static void SimulateFastAndSlowClients ()
    {
      Console.WriteLine ("Running clients...");

      // Start the slow clients...
      for (int i = 0; i < 100 * Environment.ProcessorCount; i++)
        new Thread (SimulateClient).Start (true);

      Thread.Sleep (100);

      // ...followed by 10 fast clients
      for (int i = 0; i < 10; i++)
        new Thread (SimulateClient).Start (false);
    }

    static void SimulateClient (object slow)
    {
      try { Client.Test ((bool)slow, "127.0.0.1", TcpPort); }
      catch (Exception ex)
      {
        Console.WriteLine ("Client exception: " + ex.Message);
      }
    }
  }
}
